/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.properties;
import java.beans.*;
import java.io.*;
import javax.swing.text.BadLocationException;
import org.openide.nodes.Node;
import org.openide.text.PositionBounds;
/** Base class for representations of elements in the
* properties files.
*
* @author Petr Jiricka
*/
public abstract class Element extends Object
implements Serializable {
/** Property change support */
private transient PropertyChangeSupport support;
/** Position of the begin and the end of the element. */
protected PositionBounds bounds;
/** Create a new element. */
protected Element(PositionBounds bounds) {
this.bounds = bounds;
}
public PositionBounds getBounds() {
return bounds;
}
/** Updates the element fields. This method is called after reparsing.
* @param bounds the carrier of new information.
*/
void update(Element elem) {
this.bounds = elem.bounds;
}
/** Fires property change event.
* @param name property name
* @param o old value
* @param n new value
*/
protected final void firePropertyChange(String name, Object o, Object n) {
if (support != null) {
support.firePropertyChange (name, o, n);
}
}
/** Adds property listener */
public synchronized void addPropertyChangeListener (PropertyChangeListener l) {
if (support == null) {
synchronized (this) {
// new test under synchronized block
if (support == null) {
support = new PropertyChangeSupport (this);
}
}
}
support.addPropertyChangeListener (l);
}
/** Removes property listener */
public void removePropertyChangeListener (PropertyChangeListener l) {
if (support != null) {
support.removePropertyChangeListener (l);
}
}
/** Prints this element (and all its subelements)
* by calling <code>bounds.setText(...)</code>
*/
public void print() {
try {
bounds.setText(printString());
}
catch (BadLocationException e) {
// PENDING
}
catch (IOException e) {
// PENDING
}
}
/** Get a string representation of the element for printing.
* @return the string
*/
public abstract String printString();
/** Get a value string of the element.
* @return the string
*/
public String toString() {
return (bounds == null) ? "(no bounds)" : "(" + bounds.getBegin().getOffset() + ", " + bounds.getEnd().getOffset() + ")";
}
// -------------------- INNER CLASSES ----------------------
/** General class for basic elements, which contain value directly */
public static abstract class Basic extends Element {
/** Parsed value of the element */
protected String value;
/** Create a new basic element. */
protected Basic(PositionBounds bounds, String value) {
super(bounds);
this.value = value;
}
/** Updates the element fields. This method is called after reparsing.
* @param bounds the carrier of new information.
*/
void update(Element elem) {
super.update(elem);
this.value = ((Basic)elem).value;
}
/** Get a string representation of the element.
* @return the string + bounds
*/
public String toString() {
return value + " " + super.toString();
}
/** Get a value of the element.
* @return the string
*/
public String getValue() {
return value;
}
/** Sets the value. Does not check if the value has changed. */
public void setValue(String value) {
this.value = value;
this.print();
}
}
/** Class for key elements */
public static class KeyElem extends Basic {
static final long serialVersionUID =6828294289485744331L;
/** Create a new key element. */
protected KeyElem(PositionBounds bounds, String value) {
super(bounds, value);
}
/** Get a string representation of the key for printing. Treats the '=' sign as a part of the key
* @return the string
*/
public String printString() {
return UtilConvert.saveConvert(value) + "=";
}
}
/** Class for value elements */
public static class ValueElem extends Basic {
static final long serialVersionUID =4662649023463958853L;
/** Create a new value element. */
protected ValueElem(PositionBounds bounds, String value) {
super(bounds, value);
}
/** Get a string representation of the value for printing. Appends end of the line after the value.
* @return the string
*/
public String printString() {
return UtilConvert.saveConvert(value) + "\n";
}
}
/** Class for comment elements. <code>null</code> values of the string are legal and indicate that the comment is empty. */
public static class CommentElem extends Basic {
static final long serialVersionUID =2418308580934815756L;
/** Create a new comment element. */
protected CommentElem(PositionBounds bounds, String value) {
super(bounds, value);
}
/** Get a string representation of the comment for printing. Makes sure every non-empty line starts with a # and
* that the last line is terminated with an end of line marker.
* @return the string
*/
public String printString() {
if (value == null || value.length() == 0)
return "";
else {
// insert #s at the beginning of the lines which contain non-blank characters
// holds the last position where we might have to insert a # if this line contains non-blanks
int candidate = 0;
StringBuffer sb = new StringBuffer(value);
for (int i=0; i<sb.length(); ) {
char aChar = sb.charAt(i++);
// new line
if (aChar == '\n') {
candidate = i;
}
else {
if ((candidate != -1) && (UtilConvert.whiteSpaceChars.indexOf(aChar) == -1)) {
// nonempty symbol
if ((aChar != '#') && (aChar != '!')) {
// insert a #
sb.insert(candidate, '#');
i++;
}
candidate = -1;
}
}
}
// append the \n if missing
if (sb.charAt(sb.length() - 1) != '\n')
sb.append('\n');
return sb.toString();
}
}
}
/** Class properties file elements, each of them contains a comment (preceding the property),
* a key and a value
*/
public static class ItemElem extends Element implements Node.Cookie {
KeyElem key;
ValueElem value;
CommentElem comment;
boolean justComment; // comment after the last property in a prop. file
// is represented by a ItemElem with justComment set to true
/** Parent of this element - active element has a non-null parent. */
private PropertiesStructure parent;
/** Name of the Key property */
public static final String PROP_ITEM_KEY = "key";
/** Name of the Value property */
public static final String PROP_ITEM_VALUE = "value";
/** Name of the Comment property */
public static final String PROP_ITEM_COMMENT = "comment";
static final long serialVersionUID =1078147817847520586L;
/** Create a new basic element. <code>key</code> and <code>value</code> may be null,
* then <code>justComment</code> will be set to true.
*/
protected ItemElem(PositionBounds bounds, KeyElem key, ValueElem value, CommentElem comment) {
super(bounds);
this.key = key;
this.value = value;
this.comment = comment;
justComment = (key == null);
}
/** Sets the parent of this element. */
void setParent(PropertiesStructure ps) {
parent = ps;
}
/** Returns parent if not null */
public PropertiesStructure getParent() {
if (parent == null)
throw new InternalError();
return parent;
}
/** Get a value string of the element.
* @return the string
*/
public String toString() {
return comment.toString() + '\n' +
((key == null) ? "" : key.toString()) + '\n' +
((value == null) ? "" : value.toString()) + '\n';
}
/** Returns the key element for this item. */
public KeyElem getKeyElem() {
return key;
}
/** Returns the value element for this item. */
public ValueElem getValueElem() {
return value;
}
/** Returns the comment element for this item. */
public CommentElem getCommentElem() {
return comment;
}
/** Updates the element fields. This method is called after reparsing.
* @param bounds the carrier of new information.
*/
void update(Element elem) {
super.update(elem);
if (this.key == null)
this.key = ((ItemElem)elem).key;
else
this.key.update(((ItemElem)elem).key);
if (this.value == null)
this.value = ((ItemElem)elem).value;
else
this.value.update(((ItemElem)elem).value);
this.comment.update(((ItemElem)elem).comment);
this.justComment = ((ItemElem)elem).justComment;
}
/** Get a string representation of the element for printing.
* @return the string
*/
public String printString() {
return comment.printString() +
((key == null) ? "" : key.printString()) +
((value == null) ? "" : value.printString());
}
/** Get a key by which to identify this record */
public String getKey() {
return (key == null) ? "" : key.getValue();
}
/** Set the key for this item
* @param key the new key
*/
public void setKey(String newKey) {
String oldKey = key.getValue();
if (!oldKey.equals(newKey)) {
key.setValue(newKey);
getParent().itemKeyChanged(oldKey, this);
this.firePropertyChange(PROP_ITEM_KEY, oldKey, newKey);
}
}
/** Get the value of this item */
public String getValue() {
return (value == null) ? "" : value.getValue();
}
/** Set the value of this item
* @param newValue the new value
*/
public void setValue(String newValue) {
String oldValue = value.getValue();
if (!oldValue.equals(newValue)) {
value.setValue(newValue);
getParent().itemChanged(this);
this.firePropertyChange(PROP_ITEM_VALUE, oldValue, newValue);
}
}
/** Get the comment for this item */
public String getComment() {
return (comment == null) ? "" : comment.getValue();
}
/** Set the comment for this item
* @param newComment the new comment
*/
public void setComment(String newComment) {
String oldComment = comment.getValue();
if (!oldComment.equals(newComment)) {
comment.setValue(newComment);
getParent().itemChanged(this);
this.firePropertyChange(PROP_ITEM_COMMENT, oldComment, newComment);
}
}
/** Checks for equality of two ItemElem-s */
public boolean equals(Object item) {
if (item == null)
return false;
ItemElem ie = (ItemElem)item;
if (getKey() .equals(ie.getKey() ) &&
getValue() .equals(ie.getValue() ) &&
getComment().equals(ie.getComment()))
return true;
return false;
}
} // end of inner class ItemElem
}
/*
* <<Log>>
* 11 Gandalf 1.10 11/27/99 Patrik Knakal
* 10 Gandalf 1.9 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun
* Microsystems Copyright in File Comment
* 9 Gandalf 1.8 8/18/99 Petr Jiricka Nothing
* 8 Gandalf 1.7 8/9/99 Petr Jiricka Removed debug prints
* 7 Gandalf 1.6 6/16/99 Petr Jiricka
* 6 Gandalf 1.5 6/10/99 Petr Jiricka
* 5 Gandalf 1.4 6/9/99 Ian Formanek ---- Package Change To
* org.openide ----
* 4 Gandalf 1.3 6/6/99 Petr Jiricka
* 3 Gandalf 1.2 5/14/99 Petr Jiricka
* 2 Gandalf 1.1 5/13/99 Petr Jiricka
* 1 Gandalf 1.0 5/12/99 Petr Jiricka
* $
*/